(ns lr.scene
  (:require lr.image)
  (:use lr.ray))

;;Helper math funcs
(defn sin [x] (java.lang.Math/sin x))
(defn cos [x] (java.lang.Math/cos x))


;     				     |
;    		               /-----|
;    		     /---------      |
; eye  	   /---------  	             | plane
;    ------		             |
; 	 \-----		     ( )     |
;	       \-----	             |
;		     \-----          |
;			   \-----    |
;				 \-- |

(def plane-distance 1) ;;Distance of center plane to eye
(def eye [0, 10 ,-20])
(def lookat [0,0,0]) ;; Camera is looking at this point

(def camera-dir (let [dir (v- lookat eye)]
		  (normalize dir))) ;;Unitary camera direction vec

; Rotation angles for the camera based on the camera-dir vector
; Do the math:
; A is the rotation matrix. A*[0,0,1] = camera-dir.
; Infer angles from result.
(def rotY (java.lang.Math/asin (first camera-dir)))
(def rotX (let [cosB (cos rotY)]
	    (if (not= 0 cosB)
	      (- (/ (second camera-dir) cosB))
	      (do (println "No tengo idea de quien es rotX!!")
		  (System/exit -1)))))


;;Translating by this vector is the equivalent
;;of translating by eye-[0,0,plane-distance] in global coords.
(comment (def translate-vector (v- eye (v*-scalar [(sin rotY)
					  (- (* (sin rotX)
						(cos rotY)))
					  (* (cos rotX)
					     (cos rotY))]
					 plane-distance))))


;;TODO: Make smarter.
(def translate-vector (let [dir (v- lookat eye)]
			(v+ eye (v*-scalar dir (* (/ 1 (norm dir))
						  plane-distance)))))


; eye: 0,5,10
; lookat: 0,0,0
;rotY
;0.0
;rotX
;0.4472135954999579

;; Distance from eye to plane.
;  [ - - 1 unit -  -]
;  __________________
;  |                |
;  |                |
;  |     screen     |
;  |                |
;  |________________|
;

;; 1 pixel equals this many units of measurement.
(def pixel-to-unit-ratio-x (double (/ 1 lr.image/width)))
(def pixel-to-unit-ratio-y (double (* (/ lr.image/height lr.image/width)
				      pixel-to-unit-ratio-x)))


;; Problem:
;;   Given a tuple of pixels, give a point in the plane
;; in global coordinates.
;; That is, convert to units of measure, then rotate,
;; then translate.
;; We take our camera rotations as a Y rotation followed by a X rotation,
;; Y is the up axis.
(defn pixel-to-vector [ px  py]
  (let [cx (int (- px (/ lr.image/width 2)))
	cy (int (-(- py (/ lr.image/height 2)))) ;;Centered pixel
	x (* cx (double pixel-to-unit-ratio-x))
	y (* cy (double pixel-to-unit-ratio-y)) ;; x and y in measure units.

	x (* x (cos rotY))
	y (+ (* x (sin rotX) (sin rotY))
	     (* y (cos rotX)))         ;; x and y are rotated.
	z (- (* y (sin rotX))
	     (* x (cos rotX) (sin rotY)))
	]
    ;;Rotation is done.
    ;;Now we translate
    (v+ [x y z] translate-vector))) ;;TODO: this trans. vector is wrong.


(defn ray-from-eye [x y]
  (struct ray eye (v- (pixel-to-vector x y)
		      eye)))


(defstruct object :intersection-fn )
(defstruct scene :obj-list) ;;This will be smarter, kd-trees?

(defn set-pixel [i r g b]
  (let [i (int i)]
    (aset lr.image/image-data (unchecked-multiply i (int 3)) r)
    (aset lr.image/image-data (unchecked-inc
			       (unchecked-multiply i (int 3))) g)
    (aset lr.image/image-data (unchecked-add
			       (unchecked-multiply i (int 3)) (int 2)) b)))


(defn render-scene [scene]
  (let [x (int 0)
	y (int 0)
	w (int lr.image/width)
	h (int lr.image/height)]
    (dotimes [i lr.image/num-pixels]
      (let [x (unchecked-remainder i w)
	    y (unchecked-divide i w)]
	;;Clear
	(set-pixel i 0 0 0)
	;;For each object in scene
	(doseq [obj (:obj-list scene)]
	  (let [func (:intersection-fn obj)
		ray (ray-from-eye x y)
		color (func ray)]
	    (when (not= color false)
	      (apply (partial set-pixel i) color))))))))


